package com.xpec.c4.db.usertype;

import java.io.Serializable;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Types;

import org.apache.commons.lang3.ObjectUtils;
import org.hibernate.HibernateException;
import org.hibernate.usertype.UserType;

import com.google.gson.Gson;
import com.google.gson.GsonBuilder;

public abstract class GenericJsonStringType<T> implements UserType {
	 	private static final int[] SQL_TYPES = { Types.LONGVARCHAR };

	    protected final Type userType;

	    protected final Gson gson;

	    public GenericJsonStringType() {	
	        Type type = getClass().getGenericSuperclass();
	        ParameterizedType pt = (ParameterizedType) type;
	        userType = pt.getActualTypeArguments()[0];
	        gson = new GsonBuilder().create();
	    }

	    @Override
	    public int[] sqlTypes() {
	        return SQL_TYPES;
	    }

	    @SuppressWarnings("rawtypes")
	    @Override
	    public Class returnedClass() {
	        if (userType instanceof Class) {
	            return (Class) userType;
	        } else {
	            return (Class) ((ParameterizedType) userType).getRawType();
	        }
	    }

	    @Override
		public int hashCode(Object arg0) throws HibernateException {
	    	return arg0.hashCode();
		}

		@Override
		public boolean equals(Object arg0, Object arg1) throws HibernateException {
			return ObjectUtils.equals(arg0, arg1);
		}

		@Override
		public Object nullSafeGet(ResultSet arg0, String[] arg1, Object arg2) throws HibernateException, SQLException {
	    	 String jsonString = arg0.getString(arg1[0]);
		     return gson.fromJson(jsonString, userType);
		}

	@Override
	public void nullSafeSet(PreparedStatement arg0, Object arg1, int arg2) throws HibernateException, SQLException {
		if (arg1 == null) {
			arg0.setString(arg2, null);
		} else {
			arg0.setString(arg2, gson.toJson(arg1, userType));
		}
	}

	    @Override
	    public boolean isMutable() {
	        return true;
	    }
	    
		@Override
		public Object deepCopy(Object arg0) throws HibernateException {
			return gson.fromJson(gson.toJson(arg0, userType), userType);

		}

		@Override
		public Object assemble(Serializable arg0, Object arg1) throws HibernateException {
			 return gson.fromJson((String) arg0, userType);
		}

		@Override
		public Serializable disassemble(Object arg0) throws HibernateException {
			 return gson.toJson(arg0, userType);
		}

		@Override
		public Object replace(Object arg0, Object arg1, Object arg2) throws HibernateException {
			return arg0;
		}
}
